# -*- coding: utf-8 -*-
r"""
Ariki-Koike Algebras

The *Ariki-Koike algebras* were introduced by Ariki and Koike [AK1994]_ as
a natural generalization of the Iwahori-Hecke algebras of types `A` and `B`
(see :class:`~sage.algebras.iwahori_hecke_algebra.IwahoriHeckeAlgebra`).
Soon afterwards,  Broué and Malle defined analogues of the Hecke
algebras for all complex reflection groups

Fix non-negative integers `r` an `n`. The Ariki-Koike algebras are
deformations of the group algebra of the complex reflection group
`G(r, 1, n) = \ZZ / r\ZZ \wr \mathfrak{S}_n`. If `R` is a ring containing a
*Hecke parameter* `q` and *cyclotomic parameters* `u_0, \ldots, u_{r-1}` then
the Ariki-Koike algebra `H_n(q, u_1, \ldots, u_r)` is the unital associative
`r`-algebra with generators `T_0, T_1, \ldots, T_{n-1}` an relations:

.. MATH::

    \begin{aligned}
        \prod_{i=0}^{r-1} (T_0 - u_i) & = 0, \\
        T_i^2 & = (q - 1) T_i + q && \text{for } 1 \leq i < n, \\
        T_0 T_1 T_0 T_1 & = T_1 T_0 T_1 T_0, \\
        T_i T_j & = T_j T_i && \text{if } |i - j| \geq 2, \\
        T_i T_{i+1} T_i & = T_{i+1} T_i T_{i+1} && \text{for } 1 \leq i < n.
    \end{aligned}

AUTHORS:

- Travis Scrimshaw (2016-04): initial version
- Andrew Mathas (2016-07): improved multiplication code

REFERENCES:

- [AK1994]_
- [BM1993]_
- [MM1998]_
"""

#*****************************************************************************
#  Copyright (C) 2016-2018 Travis Scrimshaw <tcscrims at gmail.com>
#                2016-2018 Andrew Mathas <andrew.mathas at sydney.edu.au>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#                  http://www.gnu.org/licenses/
#*****************************************************************************


from sage.misc.cachefunc import cached_method
from sage.misc.lazy_attribute import lazy_attribute
from sage.misc.misc_c import prod
from sage.misc.bindable_class import BindableClass
from sage.structure.parent import Parent
from sage.structure.unique_representation import UniqueRepresentation
from sage.categories.algebras import Algebras
from sage.categories.rings import Rings
from sage.categories.realizations import Realizations, Category_realization_of_parent
from sage.categories.cartesian_product import cartesian_product
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
from sage.rings.integer_ring import ZZ
from sage.combinat.free_module import CombinatorialFreeModule
from sage.combinat.permutation import Permutations
from sage.sets.family import Family
from sage.data_structures.blas_dict import iaxpy

# ABC for basis classes
class _Basis(CombinatorialFreeModule, BindableClass):
    r"""
    Abstract base class for bases of the Ariki-Koike algebra.
    """
    def __init__(self, algebra, prefix='AK'):
        r"""
        Initialize ``self``.

        EXAMPLES::

            sage: LT = algebras.ArikiKoike(2, 3).LT()
            sage: TestSuite(LT).run()
        """
        self._r = algebra._r
        self._n = algebra._n
        self._q = algebra._q
        self._u = algebra._u
        # It seems more efficient to copy this as we need it a lot
        self._zero_tuple = tuple([0] * self._n)
        self._Pn = Permutations(self._n)
        self._one_perm = self._Pn.one()
        C = cartesian_product([range(self._r)] * self._n)
        indices = cartesian_product([C, self._Pn])
        CombinatorialFreeModule.__init__(self, algebra.base_ring(), indices,
                                         prefix=prefix,
                                         category=algebra._BasesCategory())

    @cached_method
    def one_basis(self):
        r"""
        Return the index of the basis element of `1`.

        EXAMPLES::

            sage: LT = algebras.ArikiKoike(5, 3).LT()
            sage: LT.one_basis()
            ((0, 0, 0), [1, 2, 3])

            sage: T = algebras.ArikiKoike(5, 3).T()
            sage: T.one_basis()
            ((0, 0, 0), [1, 2, 3])
        """
        return (self._zero_tuple, self._one_perm)

class ArikiKoikeAlgebra(Parent, UniqueRepresentation):
    r"""
    The Ariki-Koike algebra `H_{r,n}(q, u)`.

    Let `R` be an unital integral domain.
    Let `q, u_0, \ldots, u_{r-1} \in R` such that `q^{-1} \in R`.
    The *Ariki-Koike algebra* is the unital associative algebra
    `H_{r,n}(q, u)` generated by `T_0, \ldots, T_{n-1}` that satisfies
    the following relations:

    .. MATH::

        \begin{aligned}
            \prod_{i=0}^{r-1} (T_0 - u_i) & = 0, \\
            T_i^2 & = (q - 1) T_i + q && \text{for } 1 \leq i < n, \\
            T_0 T_1 T_0 T_1 & = T_1 T_0 T_1 T_0, \\
            T_i T_j & = T_j T_i && \text{if } |i - j| \geq 2, \\
            T_i T_{i+1} T_i & = T_{i+1} T_i T_{i+1} && \text{for } 1 \leq i < n.
        \end{aligned}

    The parameter `q` is called the *Hecke parameter* and the parameters
    `u_0, \ldots, u_{r-1}` are called the *cyclotomic parameters*.
    Thus, the Ariki-Koike algebra is a deformation of the group algebra of the
    complex reflection group `G(r, 1, n) = \ZZ / r\ZZ \wr \mathfrak{S}_n`.

    Next, we define *Jucys-Murphy elements*

    .. MATH::

        L_i = q^{-i+1} T_{i-1} \cdots T_1 T_0 T_1 \cdots T_{i-1}

    for `1 \leq i \leq n`.

    .. NOTE::

        These element differ by a power of `q` from the corresponding
        elements in [AK1994]_. However, these elements are more commonly
        used because they lead to nicer representation theoretic formulas.

    Ariki and Koike [AK1994]_ showed that `H_{r,n}(q, u)` is a free
    `R`-module with a basis given by

    .. MATH::

        \{ L_1^{c_i} \cdots L_n^{c_n} T_w \mid w \in S_n, 0 \leq c_i < r \}.

    In particular, we have `\dim H_{r,n}(q,u) = r^n n! = |G(r, 1, n)|`.
    Moreover, we have `L_i L_j = L_i L_j` for all `1 \leq i, j \leq n`.

    The Ariki-Koike algebra `H_{r,n}(q, u)` can be considered as a quotient
    of the group algebra of the braid group for `G(r, 1, n)` by the ideal
    generated by `\prod_{i=0}^{r-1} (T_0 - u_i)` and `(T_i - q)(T_i + 1)`.
    Furthermore, `H_{r,n}(q, u)` can be constructed as a quotient of the
    extended affine Hecke algebra of type `A_{n-1}^{(1)}` by
    `\prod_{i=0}^{r-1} (X_1 - u_i)`.

    Since the Ariki-Koike algebra is a quotient of the group
    algebra of the braid group of `G(r, 1, n)`, we can recover
    the group algebra of `G(r, 1, n)` as follows. Consider
    `u = (1, \zeta_r, \ldots, \zeta_r^{r-1})`, where `\zeta_r`
    is a primitive `r`-th root of unity, then we have

    .. MATH::

        R G(r, 1, n) = H_{r,n}(1, u).

    INPUT:

    - ``r`` -- the maximum power of `L_i`
    - ``n`` -- the rank `S_n`
    - ``q`` -- (optional) an invertible element in a commutative ring;
      the default is `q \in R[q,q^{-1}]`, where `R` is the ring containing
      the variables ``u``
    - ``u`` -- (optional) the variables `u_1, \ldots, u_r`; the
      default is the generators of `\ZZ[u_1, \ldots, u_r]`
    - ``R`` -- (optional) a commutative ring containing ``q`` and ``u``;
      the default is the parent of `q` and `u_1, \ldots, u_r`

    EXAMPLES:

    We start by constructing an Ariki-Koike algebra where the
    values `q, u` are generic and do some computations::

        sage: H = algebras.ArikiKoike(3, 4)

    Next, we do some computations using the `LT` basis::

        sage: LT = H.LT()
        sage: LT.inject_variables()
        Defining L1, L2, L3, L4, T1, T2, T3
        sage: T1 * T2 * T1 * T2
        q*T[2,1] - (1-q)*T[2,1,2]
        sage: T1 * L1 * T2 * L3 * T1 * T2
        -(q-q^2)*L2*L3*T[2] + q*L1*L2*T[2,1] - (1-q)*L1*L2*T[2,1,2]
        sage: L1^3
        u0*u1*u2 + ((-u0*u1-u0*u2-u1*u2))*L1 + ((u0+u1+u2))*L1^2
        sage: L3 * L2 * L1
        L1*L2*L3
        sage: u = LT.u()
        sage: q = LT.q()
        sage: (q + 2*u[0]) * (T1 * T2) * L3
        (-2*u0+(2*u0-1)*q+q^2)*L3*T[1] + (-2*u0+(2*u0-1)*q+q^2)*L2*T[2]
         + (2*u0+q)*L1*T[1,2]

    We check the defining relations::

        sage: prod(L1 - val for val in u) == H.zero()
        True
        sage: L1 * T1 * L1 * T1 == T1 * L1 * T1 * L1
        True
        sage: T1 * T2 * T1 == T2 * T1 * T2
        True
        sage: T2 * T3 * T2 == T3 * T2 * T3
        True
        sage: L2 == q^-1 * T1 * L1 * T1
        True
        sage: L3 == q^-2 * T2 * T1 * L1 * T1 * T2
        True

    We construct an Ariki-Koike algebra with `u = (1, \zeta_3, \zeta_3^2)`,
    where `\zeta_3` is a primitive third root of unity::

        sage: F = CyclotomicField(3)
        sage: zeta3 = F.gen()
        sage: R.<q> = LaurentPolynomialRing(F)
        sage: H = algebras.ArikiKoike(3, 4, q=q, u=[1, zeta3, zeta3^2], R=R)
        sage: H.LT().inject_variables()
        Defining L1, L2, L3, L4, T1, T2, T3
        sage: L1^3
        1
        sage: L2^3
        1 - (q^-1-1)*T[1] - (q^-1-1)*L1*L2^2*T[1] - (q^-1-1)*L1^2*L2*T[1]

    Next, we additionally take `q = 1` to obtain the group algebra
    of `G(r, 1, n)`::

        sage: F = CyclotomicField(3)
        sage: zeta3 = F.gen()
        sage: H = algebras.ArikiKoike(3, 4, q=1, u=[1, zeta3, zeta3^2], R=F)
        sage: LT = H.LT()
        sage: LT.inject_variables()
        Defining L1, L2, L3, L4, T1, T2, T3
        sage: A = ColoredPermutations(3, 4).algebra(F)
        sage: s1, s2, s3, s0 = list(A.algebra_generators())
        sage: all(L^3 == LT.one() for L in LT.L())
        True
        sage: J = [s0, s3*s0*s3, s2*s3*s0*s3*s2, s1*s2*s3*s0*s3*s2*s1]
        sage: all(Ji^3 == A.one() for Ji in J)
        True
    """
    @staticmethod
    def __classcall_private__(cls, r, n, q=None, u=None, R=None):
        r"""
        Standardize input to ensure a unique representation.

        TESTS::

            sage: H1 = algebras.ArikiKoike(4, 3)
            sage: S = PolynomialRing(ZZ, 'u', 4)
            sage: R.<q> = LaurentPolynomialRing(S)
            sage: H2 = algebras.ArikiKoike(4, 3, q=q)
            sage: H3 = algebras.ArikiKoike(4, 3, q, S.gens(), R)
            sage: H1 is H2
            True
            sage: H2 is H3
            True
        """
        if u is None:
            if q is not None:
                R = q.parent()
            if R is None:
                R = PolynomialRing(ZZ, 'u', r)
                u = R.gens()
                if q is None:
                    R = LaurentPolynomialRing(R, 'q')
                    q = R.gen()
            else:
                u = PolynomialRing(ZZ, 'u', r).gens()
                if q is None:
                    q = 'q'
        else:
            if not isinstance(u, (list,tuple)):
                u = [u]*r
            if R is None:
                from sage.structure.element import get_coercion_model
                cm = get_coercion_model()
                if q is None:
                    R = cm.common_parent(*[val.parent() for val in u])
                    R = LaurentPolynomialRing(R, 'q')
                    q = R.gen()
                else:
                    R = cm.common_parent(q.parent(), *[val.parent() for val in u])
            elif q is None:
                q = 'q'
            u = [R(val) for val in u]
        if R not in Rings().Commutative():
            raise TypeError("base ring must be a commutative ring")
        q = R(q)
        u = tuple(u)
        return super(ArikiKoikeAlgebra, cls).__classcall__(cls, r, n, q, u, R)

    def __init__(self, r, n, q, u, R):
        r"""
        Initialize ``self``.

        EXAMPLES::

            sage: H = algebras.ArikiKoike(5, 3)
            sage: TestSuite(H).run()
            sage: H = algebras.ArikiKoike(1, 4)
            sage: TestSuite(H).run()
            sage: H = algebras.ArikiKoike(2, 3)
            sage: TestSuite(H).run()
            sage: H = algebras.ArikiKoike(3, 4)
            sage: TestSuite(H).run() # long time
        """
        self._r = r
        self._n = n
        self._q = q
        self._u = u
        self._category = Algebras(R).FiniteDimensional().WithBasis()
        Parent.__init__(self, base=R, category=self._category.WithRealizations())

        T = self.T()
        LT = self.LT()
        T.module_morphism(LT._from_T_basis, codomain=LT).register_as_coercion()
        LT.module_morphism(T._from_LT_basis, codomain=T).register_as_coercion()

    def _repr_(self):
        r"""
        Return a string representation of ``self``.

        EXAMPLES::

            sage: algebras.ArikiKoike(5, 2)
            Ariki-Koike algebra of rank 5 and order 2
             with q=q and u=(u0, u1, u2, u3, u4)
             over Univariate Laurent Polynomial Ring in q
             over Multivariate Polynomial Ring in u0, u1, u2, u3, u4
             over Integer Ring
        """
        return "Ariki-Koike algebra of rank {} and order {} with q={} and u={} over {}".format(
            self._r, self._n, self._q, self._u, self.base_ring())

    def _latex_(self):
        r"""
        Return a latex representation of ``self``.

        EXAMPLES::

            sage: H = algebras.ArikiKoike(5, 2)
            sage: latex(H)
            \mathcal{H}_{5,2}(q)
        """
        return "\\mathcal{H}_{%s,%s}(%s)"%(self._r, self._n, self._q)

    def hecke_parameter(self):
        r"""
        Return the Hecke parameter `q` of ``self``.

        EXAMPLES::

            sage: H = algebras.ArikiKoike(5, 3)
            sage: H.hecke_parameter()
            q
        """
        return self._q

    q = hecke_parameter

    def cyclotomic_parameters(self):
        r"""
        Return the cyclotomic parameters `u` of ``self``.

        EXAMPLES::

            sage: H = algebras.ArikiKoike(5, 3)
            sage: H.cyclotomic_parameters()
            (u0, u1, u2, u3, u4)
        """
        return self._u

    u = cyclotomic_parameters

    def a_realization(self):
        r"""
        Return a realization of ``self``.

        EXAMPLES::

            sage: H = algebras.ArikiKoike(5, 2)
            sage: H.a_realization()
            Ariki-Koike algebra of rank 5 and order 2
             with q=q and u=(u0, u1, u2, u3, u4) ... in the LT-basis
        """
        return self.LT()

    class _BasesCategory(Category_realization_of_parent):
        r"""
        The category of bases of a Ariki-Koike algebra.
        """
        def __init__(self, base):
            r"""
            Initialize ``self``.

            INPUT:

            - ``base`` -- a Ariki-Koike algebra

            TESTS::

                sage: H = algebras.ArikiKoike(5, 2)
                sage: bases = H._BasesCategory()
                sage: H.T() in bases
                True
            """
            Category_realization_of_parent.__init__(self, base)

        def super_categories(self):
            r"""
            The super categories of ``self``.

            EXAMPLES::

                sage: H = algebras.ArikiKoike(5, 2)
                sage: bases = H._BasesCategory()
                sage: bases.super_categories()
                [Category of realizations of Ariki-Koike algebra of rank 5 and order 2
                    with q=q and u=(u0, u1, u2, u3, u4) over ...,
                 Category of finite dimensional algebras with basis over ...]

            """
            return [Realizations(self.base()), self.base()._category]

        def _repr_(self):
            r"""
            Return the representation of ``self``.

            EXAMPLES::

                sage: H = algebras.ArikiKoike(5, 2)
                sage: H._BasesCategory()
                Category of bases of Ariki-Koike algebra of rank 5 and order 2
                 with q=q and u=(u0, u1, u2, u3, u4) over ...
            """
            return "Category of bases of %s" % self.base()

        class ParentMethods:
            r"""
            This class collects code common to all the various bases. In most
            cases, these are just default implementations that will get
            specialized in a basis.
            """
            def _repr_(self):
                r"""
                Text representation of this basis of Iwahori-Hecke algebra.

                EXAMPLES::

                    sage: H = algebras.ArikiKoike(5, 2)
                    sage: H.T()
                    Ariki-Koike algebra of rank 5 and order 2
                     with q=q and u=(u0, u1, u2, u3, u4) ... in the T-basis
                    sage: H.LT()
                    Ariki-Koike algebra of rank 5 and order 2
                     with q=q and u=(u0, u1, u2, u3, u4) ... in the LT-basis
                """
                return "%s in the %s-basis"%(self.realization_of(), self._realization_name())

            def hecke_parameter(self):
                r"""
                Return the Hecke parameter `q` of ``self``.

                EXAMPLES::

                    sage: LT = algebras.ArikiKoike(5, 3).LT()
                    sage: LT.hecke_parameter()
                    q
                """
                return self._q

            q = hecke_parameter

            def cyclotomic_parameters(self):
                r"""
                Return the cyclotomic parameters `u` of ``self``.

                EXAMPLES::

                    sage: LT = algebras.ArikiKoike(5, 3).LT()
                    sage: LT.cyclotomic_parameters()
                    (u0, u1, u2, u3, u4)
                """
                return self._u

            u = cyclotomic_parameters

            @cached_method
            def gens(self):
                r"""
                Return the generators of ``self``.

                EXAMPLES::

                    sage: LT = algebras.ArikiKoike(5, 3).LT()
                    sage: LT.gens()
                    (L1, L2, L3, T[1], T[2])
                """
                return tuple(self.algebra_generators())

            def dimension(self):
                r"""
                Return the dimension of ``self``.

                The dimension of `H_{r,n}(q, u)` is `r^n n!`.

                EXAMPLES::

                    sage: LT = algebras.ArikiKoike(8, 3).LT()
                    sage: LT.dimension()
                    3072
                    sage: LT = algebras.ArikiKoike(6, 3).LT()
                    sage: LT.dimension()
                    1296
                    sage: LT = algebras.ArikiKoike(3, 5).LT()
                    sage: LT.dimension()
                    29160
                """
                from sage.arith.all import factorial
                return self._r**self._n * factorial(self._n)

            def some_elements(self):
                r"""
                Return a list of elements of ``self``.

                EXAMPLES::

                    sage: LT = algebras.ArikiKoike(4, 3).LT()
                    sage: LT.some_elements()
                    [1 + 2*T[2] + 3*T[1] + T[2,1],
                     L1, L2, L3, T[1], T[2], L1^2, L2^2]
                """
                G = self.algebra_generators()
                elts = [self.an_element()] + list(G)
                elts += [self.L(1)**2]
                if self._n > 1:
                    elts += [self.L(2)**(self._r//2)]
                return elts

    # -----------------------------------------------------
    # Basis classes
    # -----------------------------------------------------

    class LT(_Basis):
        r"""
        The basis of the Ariki-Koike algebra given by monomials of the
        form `L T`, where `L` is product of Jucys-Murphy elements and
        `T` is a product of `\{ T_i | 0 < i < n \}`.

        This was the basis defined in [AK1994]_ except using the
        renormalized Jucys-Murphy elements.
        """
        def __init__(self, algebra):
            r"""
            Initialize ``self``.

            EXAMPLES::

                sage: LT = algebras.ArikiKoike(5, 3).LT()
                sage: TestSuite(LT).run()
                sage: LT = algebras.ArikiKoike(1, 4).LT()
                sage: TestSuite(LT).run()
                sage: LT = algebras.ArikiKoike(2, 3).LT()
                sage: TestSuite(LT).run()
                sage: LT = algebras.ArikiKoike(3, 4).LT()
                sage: TestSuite(LT).run()  # long time
            """
            _Basis.__init__(self, algebra, prefix='LT')
            self._assign_names(self.algebra_generators().keys())

        def _repr_term(self, m):
            r"""
            Return a string representation of the basis element indexed by ``m``.

            EXAMPLES::

                sage: LT = algebras.ArikiKoike(4, 3).LT()
                sage: LT._repr_term( ((1, 0, 2), Permutation([3,2,1])) )
                'L1*L3^2*T[2,1,2]'
            """
            gen_str = lambda e: '' if e == 1 else '^%s'%e
            lhs = '*'.join('L%s'%(j+1) + gen_str(i)
                           for j,i in enumerate(m[0]) if i > 0)
            redword = m[1].reduced_word()
            if not redword:
                if not lhs:
                    return '1'
                return lhs
            rhs = 'T[{}]'.format(','.join(str(i) for i in redword))
            if not lhs:
                return rhs
            return lhs + '*' + rhs

        def _latex_term(self, m):
            r"""
            Return a latex representation for the basis element indexed by ``m``.

            EXAMPLES::

                sage: LT = algebras.ArikiKoike(4, 3).LT()
                sage: LT._latex_term( ((1, 0, 2), Permutation([3,2,1])) )
                'L_{1} L_{3}^{2} T_{2} T_{1} T_{2}'
            """
            gen_str = lambda e: '' if e == 1 else '^{%s}'%e
            lhs = ' '.join('L_{%s}'%(j+1) + gen_str(i)
                           for j,i in enumerate(m[0]) if i > 0)
            redword = m[1].reduced_word()
            if not redword:
                if not lhs:
                    return '1'
                return lhs
            return lhs + ' ' + ' '.join("T_{%d}"%i for i in redword)

        def _from_T_basis(self, t):
            r"""
            Return the image of the `T` basis element indexed
            by ``t`` in ``self``.

            EXAMPLES::

                sage: H = algebras.ArikiKoike(3, 3)
                sage: LT = H.LT()
                sage: T = H.T()
                sage: all(LT(Li) == LT.L(i+1) for i,Li in enumerate(T.L()))
                True
                sage: all(LT(Ti) == LT.T(i) for i,Ti in enumerate(T.T()))
                True
                sage: all(LT(T(b)) == b for b in LT.basis())  # long time
                True

                sage: H = algebras.ArikiKoike(1, 3)
                sage: LT = H.LT()
                sage: T = H.T()
                sage: all(LT(Li) == LT.L(i+1) for i,Li in enumerate(T.L()))
                True
                sage: all(LT(T(b)) == b for b in LT.basis())  # indirect doctest
                True
            """
            # Compute the corresponding reduced word for the first part
            ret = self.one()
            T = list(self._zero_tuple)
            one = self.base_ring().one()
            for i,k in enumerate(t[0]):
                if k == 0:
                    continue
                perm = self._Pn.prod(self._Pn.simple_reflection(j)
                                     for j in range(1,i+1))
                ret = ret * self._from_dict({(self._zero_tuple, perm): one},
                                            remove_zeros=False, coerce=False)
                T[0] = k
                ret = ret * self._from_dict({(tuple(T), self._one_perm): one},
                                            remove_zeros=False, coerce=False)

            return ret * self._from_dict({(self._zero_tuple, t[1]): one},
                                         remove_zeros=False, coerce=False)

        @cached_method
        def algebra_generators(self):
            r"""
            Return the algebra generators of ``self``.

            EXAMPLES::

                sage: LT = algebras.ArikiKoike(5, 3).LT()
                sage: dict(LT.algebra_generators())
                {'L1': L1, 'L2': L2, 'L3': L3, 'T1': T[1], 'T2': T[2]}

                sage: LT = algebras.ArikiKoike(1, 4).LT()
                sage: dict(LT.algebra_generators())
                {'T1': T[1], 'T2': T[2], 'T3': T[3]}
            """
            d = {}
            if self._r != 1:
                for i in range(self._n):
                    r = list(self._zero_tuple) # Make a copy
                    r[i] = 1
                    d['L%s'%(i+1)] = self.monomial( (tuple(r), self._one_perm) )
            G = self._Pn.group_generators()
            for i in range(1, self._n):
                d['T%s'%i] = self.monomial( (self._zero_tuple, G[i]) )
            return Family(sorted(d), lambda i: d[i])

        def T(self, i=None):
            r"""
            Return the generator(s) `T_i` of ``self``.

            INPUT:

            - ``i`` -- (default: ``None``) the generator `T_i` or
              if ``None``, then the list of all generators `T_i`

            EXAMPLES::

                sage: LT = algebras.ArikiKoike(8, 3).LT()
                sage: LT.T(1)
                T[1]
                sage: LT.T()
                [L1, T[1], T[2]]
                sage: LT.T(0)
                L1
            """
            G = self.algebra_generators()
            if i is None:
                return [G['L1']] + [G['T%s'%j] for j in range(1, self._n)]
            if i == 0:
                return G['L1']
            return G['T%s'%i]

        def L(self, i=None):
            r"""
            Return the generator(s) `L_i`.

            INPUT:

            - ``i`` -- (default: ``None``) the generator `L_i` or
              if ``None``, then the list of all generators `L_i`

            EXAMPLES::

                sage: LT = algebras.ArikiKoike(8, 3).LT()
                sage: LT.L(2)
                L2
                sage: LT.L()
                [L1, L2, L3]

                sage: LT = algebras.ArikiKoike(1, 3).LT()
                sage: LT.L(2)
                u + (-u*q^-1+u)*T[1]
                sage: LT.L()
                [u,
                 u + (-u*q^-1+u)*T[1],
                 u + (-u*q^-1+u)*T[2] + (-u*q^-2+u*q^-1)*T[2,1,2]]
            """
            G = self.algebra_generators()
            if i is None:
                if self._r == 1:
                    return [self._Li_power(j, 1) for j in range(1, self._n+1)]
                return [G['L%s'%j] for j in range(1, self._n+1)]
            if self._r == 1:
                return self._Li_power(i, 1)
            return G['L%s'%i]

        @cached_method
        def product_on_basis(self, m1, m2):
            r"""
            Return the product of the basis elements indexed
            by ``m1`` and ``m2``.

            EXAMPLES::

                sage: LT = algebras.ArikiKoike(6, 3).LT()
                sage: m = ((1, 0, 2), Permutations(3)([2,1,3]))
                sage: LT.product_on_basis(m, m)
                q*L1*L2*L3^4

                sage: LT = algebras.ArikiKoike(4, 3).LT()
                sage: L1,L2,L3,T1,T2 = LT.algebra_generators()
                sage: L1 * T1 * L1^2 * T1
                q*L1*L2^2 + (1-q)*L1^2*L2*T[1]
                sage: L1^2 * T1 * L1^2 * T1
                q*L1^2*L2^2 + (1-q)*L1^3*L2*T[1]
                sage: L1^3 * T1 * L1^2 * T1
                (-u0*u1*u2*u3+u0*u1*u2*u3*q)*L2*T[1]
                 + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3)+(-u0*u1*u2-u0*u1*u3-u0*u2*u3-u1*u2*u3)*q)*L1*L2*T[1]
                 + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3)+(u0*u1+u0*u2+u1*u2+u0*u3+u1*u3+u2*u3)*q)*L1^2*L2*T[1]
                 + ((u0+u1+u2+u3)+(-u0-u1-u2-u3)*q)*L1^3*L2*T[1] + q*L1^3*L2^2

                sage: L1^2 * T1 * L1^3 * T1
                (-u0*u1*u2*u3+u0*u1*u2*u3*q)*L2*T[1]
                 + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3)+(-u0*u1*u2-u0*u1*u3-u0*u2*u3-u1*u2*u3)*q)*L1*L2*T[1]
                 + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3)+(u0*u1+u0*u2+u1*u2+u0*u3+u1*u3+u2*u3)*q)*L1^2*L2*T[1]
                 + q*L1^2*L2^3
                 + ((u0+u1+u2+u3)+(-u0-u1-u2-u3)*q)*L1^3*L2*T[1]
                 + (1-q)*L1^3*L2^2*T[1]

                sage: L1^2 * T1*T2*T1 * L2 * L3 * T2
                (q-2*q^2+q^3)*L1^2*L2*L3 - (1-2*q+2*q^2-q^3)*L1^2*L2*L3*T[2]
                 - (q-q^2)*L1^3*L3*T[1] + (1-2*q+q^2)*L1^3*L3*T[1,2]
                 + q*L1^3*L2*T[2,1] - (1-q)*L1^3*L2*T[2,1,2]

                sage: LT = algebras.ArikiKoike(2, 3).LT()
                sage: L3 = LT.L(3)
                sage: x = LT.an_element()
                sage: (x * L3) * L3 == x * (L3 * L3)
                True
            """
            # Although it is tempting to make this recursive, some care must be
            #   taken here to ensure that the various "helper" methods return
            #   linear combinations of "standard" basis elements of the form
            #   (L,w), where L is an n-tuple and w is a permutation because
            #   otherwise we may end up in an infinite loop...

            # Product is of the form L1*T1*L2*T2: separate the L's and permutations
            L1,T1 = m1
            L2,T2 = m2

            if sum(L2) == 0:
                # Compute and return the product of T1 and T2, whilst fixing L
                return self._from_dict(self._product_LTwTv(L1, T1, T2),
                                       remove_zeros=False, coerce=False)

            # If T1 is trivial then we just have L1*L2*T2 we only need to rewrite
            # all of the "large" powers that appear in L1*L2. Unfortunately, this
            # will almost certainly introduce more T_w's and it will be recursive
            # because L_n^r, for example, will introduce many powers of L_k for k<n.
            if T1 == self._one_perm:
                Lbig = list(self._zero_tuple)   # separate the "big" and small
                Lsmall = list(self._zero_tuple) # powers of the Lk's
                for i in range(self._n):
                    s = L1[i] + L2[i]
                    if s < self._r:
                        Lsmall[i] = s
                    else:
                        Lbig[i] = s
                if tuple(Lbig) == self._zero_tuple:
                    # if no big powers we only need to combine Lsmall and T2
                    return self.monomial((tuple(Lsmall), T2))

                # The l variables all commute, so we can multiply them in any order
                # that we like. For improved efficiency, however, we move the Ls to
                # the left as soon as we can. For efficiency, we multiply the
                # "big" powers in the order L_n^N L_{n-1}^N...L_1^N as this
                # way we have to expand few powers the of the Lk's later.
                return (self.monomial((tuple(Lsmall), self._one_perm))
                        * prod(self._Li_power(i+1, Lbig[i])
                               for i in reversed(range(self._n)) if Lbig[i] > 0)
                        * self.monomial((self._zero_tuple, T2))
                        )

            # If we are still here then both T1 and L2 are non-trivial. Using the
            # method _product_Tw_L we expand the product T1*L2 as a linear
            # combination of standard basis elements using the method and then,
            # recursively, multiply on the left and right by L1 and T2,
            # respectively. In other words, we multiply as L1*(T1*L2)*T2.
            return ( self.monomial((L1, self._one_perm))
                     * self._product_Tw_L(T1, L2)
                     * self.monomial((self._zero_tuple, T2)) )

        def _product_LTwTv(self, L, w, v):
            r"""
            Return the product `L * T_w * Tv` as a linear combinations of
            terms of the form `L*T_x`.

            The main point of this method is that it computes the product
            `L T_w T_v` and returns it as a linear combination of standard
            basis elements. That is, terms of the form `L T_x`. The monomial
            ``L`` does not play a role in this calculation and, instead, it
            is kept as a place holder for this "L-component" of the product.

            For this calculation the most important point is that

            .. MATH::

                T_i T_v = \begin{cases}
                    T_{s_i v},              & \text{if } \ell(s_iv) > \ell(v),\\
                    q T_{s_i v} + (q-1)T_v, & \text{if } \ell(s_iv) < \ell(v).
                \end{cases}

            This observation is used to rewrite the product `L T_w T_v`
            as a linear combination of standard basis elements.

            .. WARNING::

                This method is not intended to be called directly and, instead,
                is used by :meth:`product_on_basis`.

            INPUT:

            - ``L`` -- an `n`-tuple
            - ``w`` -- the permutation ``w``
            - ``v`` -- the permutation ``v``

            OUTPUT:

            The corresponding element represented as a ``dict``.

            EXAMPLES::

                sage: H = algebras.ArikiKoike(5, 4).LT()
                sage: P4 = Permutations(4)
                sage: H._from_dict( H._product_LTwTv((0, 3, 2, 4), P4([1,3,2,4]), P4([1,3,2,4])) )
                q*L2^3*L3^2*L4^4 - (1-q)*L2^3*L3^2*L4^4*T[2]
                sage: H._from_dict( H._product_LTwTv((0, 3, 2, 4), P4([1,3,2,4]), P4([1,3,4,2])) )
                q*L2^3*L3^2*L4^4*T[3] - (1-q)*L2^3*L3^2*L4^4*T[2,3]
                sage: H._from_dict( H._product_LTwTv((0, 3, 2, 4), P4([1,4,3,2]), P4([1,4,3,2])) )
                q^3*L2^3*L3^2*L4^4 - (q^2-q^3)*L2^3*L3^2*L4^4*T[3]
                 - (q^2-q^3)*L2^3*L3^2*L4^4*T[2]
                 + (q-2*q^2+q^3)*L2^3*L3^2*L4^4*T[2,3]
                 + (q-2*q^2+q^3)*L2^3*L3^2*L4^4*T[3,2]
                 - (1-2*q+2*q^2-q^3)*L2^3*L3^2*L4^4*T[3,2,3]
            """
            ret = {v: self.base_ring().one()}
            qm1 = self._q - self.base_ring().one()
            for i in reversed(w.reduced_word()):
                temp = {} # start from 0
                for p in ret:
                    c = ret[p]
                    # We have to flip the side due to Sage's
                    # convention for multiplying permutations
                    pi = p.apply_simple_reflection(i, side="left")
                    if p.has_descent(i, side="left"):
                        iaxpy(1, {p: c * qm1, pi: c * self._q}, temp)
                    else:
                        iaxpy(1, {pi: c}, temp)
                ret = temp
            return {(L, p): ret[p] for p in ret}

        def _product_Tw_L(self, w, L):
            r"""
            Given a permutation ``w`` and a monomial ``L`` return the product
            `T_w L` as a linear combination of terms of the form `L_v T_v`.

            To do this we write `w = s_{i_1} \cdots s_{i_k}` and then push each
            `T_{i_a}` past `L` using Lemma 3.2 of [MM1998]_ (cf. Lemma 3.3 and
            Proposition 3.4 of [AK1994]_), which says

            .. MATH::

                T_i L_i^a L_{i+1}^b = L_i^b L_{i+1}^a T_i + \begin{cases}
                  (1-q) sum_{k=0}^{a-1} L_i^{a+k} L_{i+1}^{b-k}, &\text{if } a \leq b,\\
                  (q-1) sum_{k=0}^{b-1} L_i^{b+k} L_{i+1}^{a-k}, &\text{if } a \geq b.
                \end{cases}

            Of course, `T_i` commutes with `L_k`, for `k \neq i,i+1`.

            This method is not intended to be called directly and, instead,
            is used by :meth:`product_on_basis`.

            INPUT:

            - ``w`` -- a permutation
            - ``L`` -- a tuple `(a_1, \ldots, a_n)`

            EXAMPLES::

                sage: H = algebras.ArikiKoike(5, 4).LT()
                sage: P4 = Permutations(4)
                sage: H._product_Tw_L(P4([1,3,2,4]), (0,2,2,0))
                L2^2*L3^2*T[2]
                sage: H._product_Tw_L(P4([1,3,2,4]), (0,1,3,0))
                -(1-q)*L2*L3^3 - (1-q)*L2^2*L3^2 + L2^3*L3*T[2]
                sage: H._product_Tw_L(P4([1,3,2,4]), (0,3,1,0))
                (1-q)*L2*L3^3 + L2*L3^3*T[2] + (1-q)*L2^2*L3^2
                sage: H._product_Tw_L(P4([1,3,2,4]), (2,3,1,3))
                (1-q)*L1^2*L2*L3^3*L4^3 + L1^2*L2*L3^3*L4^3*T[2] + (1-q)*L1^2*L2^2*L3^2*L4^3
            """
            # initialize wL to L: this is what we will eventually return
            wL = {(L, self._one_perm): self.base_ring().one()}
            q = self._q
            one = q.parent().one()
            for i in w.reduced_word()[::-1]:
                iL = {} # this will become T_i * L, written in standard form
                for lv in wL:
                    c = wL[lv]
                    L = list(lv[0]) # make a copy
                    v = lv[1]
                    a, b = L[i-1], L[i]
                    L[i-1], L[i] = L[i], L[i-1] # swap L_i=L[i-1] and L_{i+1}=L[i]
                    # the term L_1^{a_1} ... L_i^{a_{i+1}} L_{i+1}^{a_i} ... L_n^{a_n} T_i T_v
                    # always appears
                    iaxpy(c, self._product_LTwTv(tuple(L), self._Pn.simple_reflections()[i], v), iL) # need T_i*T_v

                    if a < b:
                        Ls = [ list(L) for k in range(b-a) ] # make copies of L
                        for k in range(b-a):
                            Ls[k][i-1] = a + k
                            Ls[k][i] = b - k
                        c *= (q - one)
                        iaxpy(1, {(tuple(l), v): c for l in Ls}, iL)

                    elif a > b:
                        Ls = [ list(L) for k in range(a-b) ] # make copies of L
                        for k in range(a-b):
                            Ls[k][i-1] = b + k
                            Ls[k][i] = a - k
                        c *= (one - q)
                        iaxpy(1, {(tuple(l), v): c for l in Ls}, iL)

                wL = iL # replace wL with iL and repeat
            return self._from_dict(wL, remove_zeros=False, coerce=False)

        @cached_method
        def _Li_power(self, i, m):
            r"""
            Return `L_i^m`, where `m \geq 0`.

            To compute `L_i^m` we use Corollary 3.4 of [MM1998]_ which says that

            .. MATH::

                L_i^m = q^{-1} T_{i-1} L_{i-1}^m T_{i-1}
                  + (1 - q^{-1}) \sum_{c=1}^{m-1} L_i^c L_{i-1}^{m-c} T_{i-1}.

            .. WARNING::

                This function is used internally by the multiplication and
                may return elements that are not in the basis. However
                these will be eventually resolved after the product has
                been computed. ::

                    sage: H = algebras.ArikiKoike(3, 2).LT()
                    sage: L2 = H.L(2)
                    sage: H._Li_power(2, 4)
                    ((u0^2*u1*u2+u0*u1^2*u2+u0*u1*u2^2)) + ...
                     - (q^-1-1)*L1*L2^3*T[1] ...
                     - (q^-1-1)*L1^3*L2*T[1]
                    sage: H._Li_power(2, 4) == L2^4
                    False
                    sage: L2 * H._Li_power(2, 4) == L2^5
                    True

            EXAMPLES::

                sage: H = algebras.ArikiKoike(3, 3).LT()
                sage: for i in range(1,4):
                ....:     for m in range(4):
                ....:         print('L_{}^{} = {}'.format(i,m,H._Li_power(i,m)))
                L_1^0 = 1
                L_1^1 = L1
                L_1^2 = L1^2
                L_1^3 = u0*u1*u2 + ((-u0*u1-u0*u2-u1*u2))*L1 + ((u0+u1+u2))*L1^2
                L_2^0 = 1
                L_2^1 = L2
                L_2^2 = L2^2
                L_2^3 = u0*u1*u2 + (-u0*u1*u2*q^-1+u0*u1*u2)*T[1]
                 + ((-u0*u1-u0*u2-u1*u2))*L2 + ((u0+u1+u2))*L2^2
                 + ((u0+u1+u2)*q^-1+(-u0-u1-u2))*L1*L2*T[1]
                 - (q^-1-1)*L1*L2^2*T[1] - (q^-1-1)*L1^2*L2*T[1]
                L_3^0 = 1
                L_3^1 = L3
                L_3^2 = L3^2
                L_3^3 = u0*u1*u2 + (-u0*u1*u2*q^-1+u0*u1*u2)*T[2]
                + (-u0*u1*u2*q^-2+u0*u1*u2*q^-1)*T[2,1,2]
                + ((-u0*u1-u0*u2-u1*u2))*L3 + ((u0+u1+u2))*L3^2
                + ((u0+u1+u2)*q^-1+(-u0-u1-u2))*L2*L3*T[2]
                - (q^-1-1)*L2*L3^2*T[2] - (q^-1-1)*L2^2*L3*T[2]
                + ((u0+u1+u2)*q^-2+(-2*u0-2*u1-2*u2)*q^-1+(u0+u1+u2))*L1*L3*T[1,2]
                + ((u0+u1+u2)*q^-2+(-u0-u1-u2)*q^-1)*L1*L3*T[2,1,2]
                - (q^-2-2*q^-1+1)*L1*L3^2*T[1,2] - (q^-2-q^-1)*L1*L3^2*T[2,1,2]
                - (q^-2-2*q^-1+1)*L1*L2*L3*T[1,2] - (q^-2-2*q^-1+1)*L1^2*L3*T[1,2]
                - (q^-2-q^-1)*L1^2*L3*T[2,1,2]
            """
            # shorthand for returning a tuple of the form (0,...,a,b,...,0) with a,b
            # in the (i-1)th and i-th positions, respectively
            def Ltuple(a, b):
                return tuple([b if j == i else a if j == i-1 else 0
                              for j in range(1,self._n+1)])

            # return "small" powers of the generators without change
            if m < self._r:
                return self.monomial( (Ltuple(0, m), self._one_perm) )

            if i > 1:
                si = self._Pn.simple_reflections()[i-1]
                qsum = self.base_ring().one() - self._q**-1
                # by calling _Li_power we avoid infinite recursion here
                return ( self.sum_of_terms( ((Ltuple(c, m-c), si), qsum) for c in range(1, m) )
                         + self._q**-1 * self.T(i-1) * self._Li_power(i-1, m) * self.T(i-1) )

            # now left with the case i = 1 and m >= r
            if m > self._r:
                return self.monomial((Ltuple(0, 1), self._one_perm)) * self._Li_power(i,m-1)

            z = PolynomialRing(self.base_ring(), 'DUMMY').gen()
            p = list(prod(z - val for val in self._u))#[:-1]
            p.pop() # remove the highest power
            zero = self.base_ring().zero()
            return self._from_dict({(Ltuple(0, exp), self._one_perm): -coeff
                                    for exp,coeff in enumerate(p) if coeff != zero},
                                   remove_zeros=False, coerce=False)

        @cached_method
        def inverse_T(self, i):
            r"""
            Return the inverse of the generator `T_i`.

            From the quadratic relation, we have

            .. MATH::

                T_i^{-1} = q^{-1} T_i + (q^{-1} - 1).

            EXAMPLES::

                sage: LT = algebras.ArikiKoike(3, 4).LT()
                sage: [LT.inverse_T(i) for i in range(1, 4)]
                [(q^-1-1) + (q^-1)*T[1],
                 (q^-1-1) + (q^-1)*T[2],
                 (q^-1-1) + (q^-1)*T[3]]

            TESTS::

                sage: LT = algebras.ArikiKoike(4, 4).LT()
                sage: all(LT.inverse_T(i) * LT.T(i) == LT.one() for i in range(1, 4))
                True
                sage: all(LT.T(i) * LT.inverse_T(i) == LT.one() for i in range(1, 4))
                True
            """
            c = ~self._q - self.base_ring().one()
            m = self.T(i).leading_support()
            return self._from_dict({m: ~self._q, self.one_basis(): c})

        class Element(CombinatorialFreeModule.Element):
            def inverse(self):
                r"""
                Return the inverse if ``self`` is a basis element.

                EXAMPLES::

                    sage: LT = algebras.ArikiKoike(3, 4).LT()
                    sage: t = LT.T(1) * LT.T(2) * LT.T(3); t
                    T[1,2,3]
                    sage: t.inverse()
                    (q^-3-3*q^-2+3*q^-1-1) + (q^-3-2*q^-2+q^-1)*T[3]
                     + (q^-3-2*q^-2+q^-1)*T[2] + (q^-3-q^-2)*T[3,2]
                     + (q^-3-2*q^-2+q^-1)*T[1] + (q^-3-q^-2)*T[1,3]
                     + (q^-3-q^-2)*T[2,1] + (q^-3)*T[3,2,1]
                """
                if len(self) != 1:
                    raise NotImplementedError("inverse only implemented for monomials")
                l,w = self.support_of_term()
                if sum(l) != 0:
                    raise NotImplementedError("inverse only implemented for monomials in T variables")
                H = self.parent()
                return ~self[l,w] * H.prod(H.inverse_T(i) for i in reversed(w.reduced_word()))

            __invert__ = inverse

    class T(_Basis):
        r"""
        The basis of the Ariki-Koike algebra given by monomials of the
        generators `\{ T_i | 0 \leq i < n \}`.

        We use the choice of reduced expression given by [BM1997]_:

        .. MATH::

            T_{1,a_1} \cdots T_{n,a_n} T_w,

        where `T_{i,k} = T_{i-1} \cdots T_2 T_1 T_0^k` (note that
        `T_{1,k} = T_0^k`) and `w` is a reduced expression of an
        element in `\mathfrak{S}_n`.
        """
        def __init__(self, algebra):
            r"""
            Initialize ``self``.

            EXAMPLES::

                sage: T = algebras.ArikiKoike(5, 3).T()
                sage: TestSuite(T).run()
                sage: T = algebras.ArikiKoike(1, 4).T()
                sage: TestSuite(T).run()
                sage: T = algebras.ArikiKoike(2, 3).T()
                sage: TestSuite(T).run()
                sage: T = algebras.ArikiKoike(3, 4).T()
                sage: TestSuite(T).run() # long time
            """
            _Basis.__init__(self, algebra, prefix='T')
            self._assign_names(['T%s'%i for i in range(self._n)])

        def _repr_term(self, t):
            r"""
            Return a string representation of the basis element indexed by ``m``.

            EXAMPLES::

                sage: T = algebras.ArikiKoike(4, 3).T()
                sage: T._repr_term( ((1,0,2), Permutation([3,2,1])) )
                'T[0,2,1,0,0,2,1,2]'
            """
            redword = []
            for i,k in enumerate(t[0]):
                if k == 0:
                    continue
                redword += list(reversed(range(1,i+1))) + [0]*k
            redword += t[1].reduced_word()
            if len(redword) == 0:
                return "1"
            return (self._print_options['prefix']
                    + '[%s]'%','.join('%d'%i for i in redword))

        def _latex_term(self, t):
            r"""
            Return a latex representation for the basis element indexed by ``m``.

            EXAMPLES::

                sage: T = algebras.ArikiKoike(4, 3).T()
                sage: T._latex_term( ((1,0,2), Permutation([3,2,1])) )
                'T_{0}T_{1}T_{0}T_{0}T_{2}T_{1}T_{2}'
            """
            redword = []
            for i,k in enumerate(t[0]):
                if k == 0:
                    continue
                redword += list(reversed(range(1,i))) + [0]*k
            redword += t[1].reduced_word()
            if len(redword) == 0:
                return "1"
            return ''.join("%s_{%d}"%(self._print_options['prefix'], i)
                           for i in redword)

        def _from_LT_basis(self, m):
            r"""
            Return the image of the `LT` basis element indexed
            by ``m`` in ``self``.

            EXAMPLES::

                sage: H = algebras.ArikiKoike(4, 2)
                sage: LT = H.LT()
                sage: T = H.T()
                sage: all(T(Li) == T.L(i+1) for i,Li in enumerate(LT.L()))
                True
                sage: all(T(Ti) == T.T(i) for i,Ti in enumerate(LT.T()))
                True

            Check that the products of elements agrees::

                sage: type_A_words = [p.reduced_word() for p in Permutations(H._n)]
                sage: def from_reduced_word(B, w):
                ....:     t = B.T()
                ....:     return B.prod(t[i] for i in w)
                sage: all(T(from_reduced_word(LT, w)) == from_reduced_word(T, w)
                ....:     for w in type_A_words)
                True

            Check that the composition of the morphisms is the identity::

                sage: all(T(LT(b)) == b for b in T.basis())  # indirect doctest
                True
            """
            ret = self.prod(self.L(i+1)**k for i,k in enumerate(m[0]))
            return ret * self.monomial( (self._zero_tuple, m[1]) )

        @cached_method
        def algebra_generators(self):
            r"""
            Return the algebra generators of ``self``.

            EXAMPLES::

                sage: T = algebras.ArikiKoike(5, 3).T()
                sage: dict(T.algebra_generators())
                {0: T[0], 1: T[1], 2: T[2]}

                sage: T = algebras.ArikiKoike(1, 4).T()
                sage: dict(T.algebra_generators())
                {1: T[1], 2: T[2], 3: T[3]}
            """
            start = 1 if self._r == 1 else 0
            return Family(list(range(start, self._n)), self.T)

        def T(self, i=None):
            r"""
            Return the generator(s) `T_i` of ``self``.

            INPUT:

            - ``i`` -- (default: ``None``) the generator `T_i` or if ``None``,
              then the list of all generators `T_i`

            EXAMPLES::

                sage: T = algebras.ArikiKoike(8, 3).T()
                sage: T.T(1)
                T[1]
                sage: T.T()
                [T[0], T[1], T[2]]

                sage: T = algebras.ArikiKoike(1, 4).T()
            """
            if i is None:
                return [self.T(j) for j in range(self._n)]

            if i == 0:
                return self.monomial( ((1,) + self._zero_tuple[1:], self._one_perm) )
            s = self._Pn.simple_reflections()
            return self.monomial( (self._zero_tuple, s[i]) )

        @cached_method
        def L(self, i=None):
            r"""
            Return the Jucys-Murphy element(s) `L_i`.

            The Jucys-Murphy element `L_i` is defined as

            .. MATH::

                L_i = q^{-i+1} T_{i-1} \cdots T_1 T_0 T_1 \cdots T_{i-1}
                = q^{-1} T_{i-1} L_{i-1} T_{i-1}.

            INPUT:

            - ``i`` -- (default: ``None``) the Jucys-Murphy element `L_i`
              or if ``None``, then the list of all `L_i`

            EXAMPLES::

                sage: T = algebras.ArikiKoike(8, 3).T()
                sage: T.L(2)
                (q^-1)*T[1,0,1]
                sage: T.L()
                [T[0], (q^-1)*T[1,0,1], (q^-2)*T[2,1,0,1,2]]

                sage: T0,T1,T2 = T.T()
                sage: q = T.q()
                sage: T.L(1) == T0
                True
                sage: T.L(2) == q^-1 * T1*T0*T1
                True
                sage: T.L(3) == q^-2 * T2*T1*T0*T1*T2
                True

                sage: T = algebras.ArikiKoike(1, 3).T()
                sage: T.L(2)
                u + (-u*q^-1+u)*T[1]
                sage: T.L()
                [u,
                 u + (-u*q^-1+u)*T[1],
                 u + (-u*q^-1+u)*T[2] + (-u*q^-2+u*q^-1)*T[2,1,2]]

            TESTS:

            Check that the Jucys-Murphy elements form a commutative
            subring::

                sage: T = algebras.ArikiKoike(8, 4).T()
                sage: L = T.L()
                sage: all(x*y == y*x for x in L for y in L)
                True

                sage: T = algebras.ArikiKoike(2, 3).T()
                sage: L = T.L()
                sage: all(x*y == y*x for x in L for y in L)
                True

                sage: T = algebras.ArikiKoike(1, 4).T()
                sage: L = T.L()
                sage: all(x*y == y*x for x in L for y in L)
                True
            """
            if i is None:
                return [self.L(j) for j in range(1, self._n+1)]

            if i == 1:
                if self._r == 1:
                    return self.from_base_ring(self._u[0])
                else:
                    return self.T(0)
            T = self.T()
            return self._q**-1 * T[i-1] * self.L(i-1) * T[i-1]

        @cached_method
        def product_on_basis(self, m1, m2):
            r"""
            Return the product of the basis elements indexed
            by ``m1`` and ``m2``.

            EXAMPLES::

                sage: T = algebras.ArikiKoike(2, 3).T()
                sage: T0, T1, T2 = T.T()
                sage: T.product_on_basis(T0.leading_support(), T1.leading_support())
                T[0,1]
                sage: T1 * T2
                T[1,2]
                sage: T2 * T1
                T[2,1]
                sage: T2 * (T2 * T1 * T0)
                -(1-q)*T[2,1,0] + q*T[1,0]
                sage: (T1 * T0 * T1 * T0) * T0
                (-u0*u1)*T[1,0,1] + ((u0+u1))*T[0,1,0,1]
                sage: (T0 * T1 * T0 * T1) * (T0 * T1)
                (-u0*u1*q)*T[1,0] + (u0*u1-u0*u1*q)*T[1,0,1]
                 + ((u0+u1)*q)*T[0,1,0] + ((-u0-u1)+(u0+u1)*q)*T[0,1,0,1]
                sage: T1 * (T0 * T2 * T1 * T0)
                T[1,0,2,1,0]
                sage: (T1 * T2) * (T2 * T1 * T0)
                -(1-q)*T[2,1,0,2] - (q-q^2)*T[1,0] + q^2*T[0]
                sage: (T2*T1*T2) * (T2*T1*T0*T1*T2)
                -(q-q^2)*T[2,1,0,1,2] + (1-2*q+q^2)*T[2,1,0,2,1,2]
                 - (q-q^2)*T[1,0,2,1,2] + q^2*T[0,2,1,2]

            We check some relations::

                sage: T0 * T1 * T0 * T1 == T1 * T0 * T1 * T0
                True
                sage: T1 * T2 * T1 == T2 * T1 * T2
                True
                sage: (T1 * T0) * T0 == T1 * (T0 * T0)
                True
                sage: (T.L(1) * T.L(2)) * T.L(2) - T.L(1) * (T.L(2) * T.L(2))
                0
                sage: (T.L(2) * T.L(3)) * T.L(3) - T.L(2) * (T.L(3) * T.L(3))
                0

            TESTS::

                sage: T = algebras.ArikiKoike(2, 3).T()
                sage: T0, T1, T2 = T.T()
                sage: (T1 * T0 * T1) * (T0 * T0)
                (-u0*u1)*T[1,0,1] + ((u0+u1))*T[0,1,0,1]
                sage: T1 * T.L(3) * T2 * T1 * T0 - T1 * (T.L(3) * T2 * T1 * T0)
                0

                sage: T = algebras.ArikiKoike(3, 3).T()
                sage: x = T.T(0) * T.T(1)
                sage: (x*x)*x == x*(x*x)
                True

                sage: T = algebras.ArikiKoike(3, 4).T()
                sage: L1 = T.L(1)
                sage: L2 = T.L(2)
                sage: (L2 * L1^2) * L2 == L2 * (L1^2 * L2)
                True
                sage: T1 = T.T(1)
                sage: (T1 * L1^2) * T1 * L1 * L1 == (T1 * L1^2) * T1 * L1^2
                True
            """
            # We represent T_i for i > 0 as S_i in comments to avoid confusion.
            # Product is of the form t1*s1 * t2*s2: separate the T's and permutations.
            t1, s1 = m1
            t2, s2 = m2
            one = self.base_ring().one()
            q = self._q
            qm1 = q - one

            # We first handle the case when s1 == 1
            if s1 == self._one_perm:
                if t1 == self._zero_tuple:
                    # Multiplying 1 * m2
                    return self._from_dict({m2: one}, remove_zeros=False)
                if t2 == self._zero_tuple:
                    return self._from_dict({(t1, s2): one}, remove_zeros=False)
                k1 = max(k for k,a in enumerate(t1) if a != 0)
                k2 = min(k for k,a in enumerate(t2) if a != 0)
                if k1 < k2:
                    T = list(t1)
                    for k in range(k2, len(t2)):
                        T[k] = t2[k]
                    return self._from_dict({(tuple(T), s2): one}, remove_zeros=False)
                # This is the most recursive part of the product
                M = self._product_TT(k1, t1[k1], k2, t2[k2])
                t1 = list(t1)
                t2 = list(t2)
                t1[k1] = 0
                t2[k2] = 0
                L = self._from_dict({(tuple(t1), self._one_perm): one}, remove_zeros=False)
                R = self._from_dict({(tuple(t2), s2): one}, remove_zeros=False)
                return L * M * R

            # The current product of T's and the type A Hecke algebra
            tprod = [( [(k, a) for k, a in enumerate(t2) if a != 0], {s2: one} )]

            # s1 through t2
            for i in reversed(s1.reduced_word()):
                new_t = []
                for index in range(len(tprod)):
                    j = i
                    T, sprod = tprod[index]
                    absorbed = False
                    for ind in range(len(T)):
                        k, a = T[ind]
                        # -1 from i since k is 0-based but i is 1-based
                        if j < k:
                            j += 1
                        elif j == k:
                            absorbed = True
                            # Quadratic relation: S_k^2 = (q - 1) S_k + q
                            # So S_{k-1} T_{k,a} = (q-1) T_{k,a} + q T_{k-1,a}
                            # Make a copy of T since we need to mutate it
                            new_t.append((list(T), {s: q * sprod[s] for s in sprod}))
                            new_t[-1][0][ind] = (k-1, a)
                            for s in sprod:
                                sprod[s] *= qm1
                            break
                        elif j == k + 1:
                            absorbed = True
                            T[ind] = (k+1, a)
                            break
                        # elif j > k: pass
                    if absorbed:
                        # We do not need to update tprod[index] because we
                        #   have mutated that pair of objects (T, sprod).
                        continue

                    # Do the usual Hecke product of S_j * S
                    temp = {} # start from 0
                    for p in sprod:
                        c = sprod[p]
                        # We have to flip the side due to Sage's
                        # convention for multiplying permutations
                        pj = p.apply_simple_reflection(j, side="left")
                        if p.has_descent(j, side="left"):
                            iaxpy(1, {p: c * qm1, pj: c * self._q}, temp)
                        else:
                            iaxpy(1, {pj: c}, temp)
                    tprod[index] = (T, temp)
                tprod.extend(new_t)

            # Compute t1 * T * sprod
            def compute(T, sprod):
                if not T: # T=1, so just do t1 * sprod, each of which is in order
                    return self._from_dict({(t1, s): sprod[s] for s in sprod},
                                           remove_zeros=False, coerce=False)

                s_elt = self._from_dict({(self._zero_tuple, s): sprod[s] for s in sprod},
                                         remove_zeros=False, coerce=False)
                # Break T into basis vectors as much as possible to best take
                #   advantage of the caching
                cur = list(t1)
                product = [cur]
                if t1 != self._zero_tuple:
                    K = max(k for k, a in enumerate(t1) if a != 0)
                else:
                    K = -1
                T.reverse() # reverse the list so we can pop off the front
                while T:
                    k, a = T.pop()
                    if k > K:
                        cur[k] = a
                    else:
                        cur = list(self._zero_tuple)
                        cur[k] = a
                        product.append(cur)
                    K = k
                return self.prod(self._from_dict({(tuple(p), self._one_perm): one},
                                                 remove_zeros=False, coerce=False)
                                 for p in product) * s_elt

            return self.sum(compute(T, sprod) for T, sprod in tprod)

        @lazy_attribute
        def _T0_polynomial(self):
            r"""
            Return `p` such that `T0^{r-1} - p = \prod_{i=0}^{r-1} (T_0 - u_i)`.

            OUTPUT:

            A ``dict`` representing the polynomial `p`.

            EXAMPLES::

                sage: T = algebras.ArikiKoike(4, 2).T()
                sage: T._T0_polynomial
                ((u0 + u1 + u2 + u3))*DUMMY^3
                 + ((-u0*u1 - u0*u2 - u1*u2 - u0*u3 - u1*u3 - u2*u3))*DUMMY^2
                 + ((u0*u1*u2 + u0*u1*u3 + u0*u2*u3 + u1*u2*u3))*DUMMY
                 - u0*u1*u2*u3
            """
            z = PolynomialRing(self.base_ring(), 'DUMMY').gen()
            # Remove the highest power
            return -prod(z - val for val in self._u).truncate(self._r)

        def _reduced_T0_power(self, exp):
            r"""
            Return the element `T_0` to the power ``exp`` in terms
            of `T_0^k` for `k < r`.

            EXAMPLES::

                sage: T = algebras.ArikiKoike(2, 3).T()
                sage: T._reduced_T0_power(1)
                1
                sage: T._reduced_T0_power(2)
                ((u0 + u1))*DUMMY - u0*u1
                sage: T._reduced_T0_power(3)
                ((u0^2 + u0*u1 + u1^2))*DUMMY + (-u0^2*u1 - u0*u1^2)
                sage: T._reduced_T0_power(4)
                ((u0^3 + u0^2*u1 + u0*u1^2 + u1^3))*DUMMY
                 + (-u0^3*u1 - u0^2*u1^2 - u0*u1^3)
                sage: T._reduced_T0_power(5)
                ((u0^4 + u0^3*u1 + u0^2*u1^2 + u0*u1^3 + u1^4))*DUMMY
                 + (-u0^4*u1 - u0^3*u1^2 - u0^2*u1^3 - u0*u1^4)
            """
            if exp < self._r:
                return self.base_ring().one()
            PR = self._T0_polynomial.parent()
            z = PR.gen()
            cur = z ** exp
            while cur.degree() >= self._r:
                cur = (PR.sum(coeff * self._T0_polynomial * z**e
                             for e, coeff in enumerate(cur.list()[self._r:]))
                       + cur.truncate(self._r))
            return cur

        @cached_method
        def _product_TT(self, kp, a, k, b):
            r"""
            Return the product `T_{k',a} T_{k,b}` with `k' \geq k` in terms
            of the basis elements of ``self``.

            From Lemma 2.3 of [BM1997]_, we have

            .. MATH::

                T_{k',a} T_{k,b} = T_{k-1,b} T_{k',a} T_1
                  + (q - 1) \sum_{i=1}^b T_{k-1,a+b-i} T_{k',i}
                                         - T_{k-1,i} T_{k',a+b-i}.

            INPUT:

            - ``kp``, ``k`` -- 0-based indices
            - ``a``, ``b`` -- the exponents of the `T_0` generator

            EXAMPLES::

                sage: T = algebras.ArikiKoike(4, 3).T()
                sage: T._product_TT(1, 0, 0, 1)
                T[1,0]
                sage: T._product_TT(1, 1, 0, 1)
                T[1,0,0]
                sage: T._product_TT(1, 2, 0, 1)
                T[1,0,0,0]
                sage: T._product_TT(1, 3, 0, 1)
                (-u0*u1*u2*u3)*T[1]
                 + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3))*T[1,0]
                 + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3))*T[1,0,0]
                 + ((u0+u1+u2+u3))*T[1,0,0,0]
                sage: T._product_TT(1, 2, 0, 2)
                (-u0*u1*u2*u3)*T[1]
                 + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3))*T[1,0]
                 + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3))*T[1,0,0]
                 + ((u0+u1+u2+u3))*T[1,0,0,0]
                sage: T._product_TT(2, 1, 0, 3)
                (-u0*u1*u2*u3)*T[2,1]
                 + ((u0*u1*u2+u0*u1*u3+u0*u2*u3+u1*u2*u3))*T[2,1,0]
                 + ((-u0*u1-u0*u2-u1*u2-u0*u3-u1*u3-u2*u3))*T[2,1,0,0]
                 + ((u0+u1+u2+u3))*T[2,1,0,0,0]

            TESTS::

                sage: H = algebras.ArikiKoike(3, 4)
                sage: T = H.T()
                sage: T._product_TT(1, 2, 1, 2)
                (-u0*u1*u2+u0*u1*u2*q)*T[1,0]
                 + (u0*u1*u2-u0*u1*u2*q)*T[0,1]
                 + ((u0+u1+u2)+(-u0-u1-u2)*q)*T[0,1,0,0]
                 + ((-u0-u1-u2)+(u0+u1+u2)*q)*T[0,0,1,0]
                 + T[0,0,1,0,0,1]
                sage: T._product_TT(2,2,2,2)
                (-u0*u1*u2+u0*u1*u2*q)*T[2,1,0,2]
                 + (u0*u1*u2-u0*u1*u2*q)*T[1,0,2,1]
                 + ((u0+u1+u2)+(-u0-u1-u2)*q)*T[1,0,2,1,0,0]
                 + ((-u0-u1-u2)+(u0+u1+u2)*q)*T[1,0,0,2,1,0]
                 + T[1,0,0,2,1,0,0,1]
                sage: T._product_TT(3,2,3,2)
                (-u0*u1*u2+u0*u1*u2*q)*T[3,2,1,0,3,2]
                 + (u0*u1*u2-u0*u1*u2*q)*T[2,1,0,3,2,1]
                 + ((u0+u1+u2)+(-u0-u1-u2)*q)*T[2,1,0,3,2,1,0,0]
                 + ((-u0-u1-u2)+(u0+u1+u2)*q)*T[2,1,0,0,3,2,1,0]
                 + T[2,1,0,0,3,2,1,0,0,1]
            """
            # Quadratic relation: S_i^2 - (q - 1) S_i - q == 0
            # [BM1997]_: S_i^2 - (q_1 + q_2) S_i + q_1 q_2 == 0
            # Implies q_1 = q, q_2 = -1
            one = self.base_ring().one()
            # Case T_{k',a} T_0^b = T_{k',a+b}
            if k == 0:
                if a + b < self._r:
                    T = list(self._zero_tuple)
                    T[kp] = a + b
                    return self._from_dict({(tuple(T), self._one_perm): one},
                                           remove_zeros=False, coerce=False)
                def key(exp):
                    if exp > 0 or kp == 0:
                        T = list(self._zero_tuple)
                        T[kp] = exp
                        return (tuple(T), self._one_perm)
                    # Note that kp is 0-based, but our 0-index in the T portion
                    #   is the power of T_0
                    perm = self._Pn.one()
                    for j in range(1, kp+1):
                        perm = perm.apply_simple_reflection_left(j)
                    return (self._zero_tuple, perm)
                p = self._reduced_T0_power(a + b)
                zero = self.base_ring().zero()
                return self._from_dict({key(exp): coeff
                                        for exp, coeff in enumerate(p)
                                        if coeff != zero},
                                       remove_zeros=False, coerce=False)

            # Otherwise k > 0
            assert kp >= k
            s1 = self._Pn.simple_reflection(1)
            qm1 = self._q - one
            T = list(self._zero_tuple)
            T[k-1] = b
            T[kp] = a
            ret = {(tuple(T), s1): one}
            zero = self.base_ring().zero()
            def T_index(exp, ind, i, indp):
                T = list(self._zero_tuple)
                T[ind] = exp
                T[indp] = i
                return tuple(T)
            for i in range(1, b+1):
                if a + b - i == i:
                    continue
                if a + b - i < self._r:
                    T[k-1] = a + b - i
                    T[kp] = i
                    m = (tuple(T), self._one_perm)
                    T[k-1] = i
                    T[kp] = a + b - i
                    mp = (tuple(T), self._one_perm)
                    iaxpy(1, {m: qm1, mp: -qm1}, ret)
                else:
                    p = self._reduced_T0_power(a + b - i)
                    temp = {(T_index(exp, k-1, i, kp), self._one_perm): qm1 * coeff
                            for exp, coeff in enumerate(p) if coeff != zero}
                    if p[0] != zero and k > 1:
                        # We need to add back in the permutation for the "T_{k-1,0}"
                        #    in the reduction from T_{k-1,a+b-i}
                        perm = self._Pn.one()
                        for j in range(2, k+1):  # Recall k is 0-based, we add 1 back from Lemma 2.3(a)
                            perm = perm.apply_simple_reflection_left(j)
                        tind = T_index(0, k-1, i, kp)
                        temp[(tind, perm)] = temp[(tind, self._one_perm)]
                        del temp[(tind, self._one_perm)]
                    iaxpy(1, temp, ret)
                    temp = {(T_index(exp, kp, i, k-1), self._one_perm): -qm1 * coeff
                            for exp, coeff in enumerate(p) if coeff != zero}
                    if p[0] != zero:
                        # We need to add back in the permutation for the "T_{k',0}"
                        #    in the reduction from T_{k',a+b-i}
                        perm = self._Pn.one()
                        for j in range(1, kp+1):  # Recall kp is 0-based
                            perm = perm.apply_simple_reflection_left(j)
                        tind = T_index(0, kp, i, k-1)
                        temp[(tind, perm)] = temp[(tind, self._one_perm)]
                        del temp[(tind, self._one_perm)]
                    iaxpy(1, temp, ret)

            return self._from_dict(ret, remove_zeros=False)

